/**
  ******************************************************************************
  * @author     Chris
  * @since      2023/10/2 19:20
  *
  * @file       hd_servo.c
  * @brief      Servo Hardware Driver.
  *
  * @note       This file contains the hardware driver for the Servo.
  *
  * @warning    None.
  ******************************************************************************
  * Change Logs:
  *   Date           Author       Notes
  *   2023-10-02     Chris        the first version
  *
  ******************************************************************************
  */


/** region declare function region */

/** region declare function region */


#include "stm32g4xx_ll_bus.h"
#include "stm32g4xx_ll_tim.h"
#include "stm32g4xx_ll_gpio.h"

/** region public function region */

/**
 * @brief   舵机初始化（TIM3 的通道 2）
 *
 * @note    PWM 控制舵机初始化，要求 PWM 周期 20ms，高电平持续 0.5ms ~ 2.5ms
 *          PWM 频率 = CK_PSC / (PSC + 1) / (ARR + 1)
 *          PWM 占空比 = CCR / (ARR + 1)
 *          PWM 分辨率 = 1 / (ARR + 1)
 * @param   无
 * @retval  无
 */
void HD_Servo_Init(void) {
    // 使能时钟
    LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM3);

    // 设置内部时钟源
    LL_TIM_SetClockSource(TIM3, LL_TIM_CLOCKSOURCE_INTERNAL);

    // 配置时基单元
    LL_TIM_InitTypeDef TIM_InitStruct = {0};
    TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;   // 滤波采样频繁的分频系数
    TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
    TIM_InitStruct.Prescaler = 170 - 1;         // PSC
    TIM_InitStruct.Autoreload = 20000 - 1;      // ARR
    TIM_InitStruct.RepetitionCounter = 0;       // 重复计数器
    LL_TIM_Init(TIM3, &TIM_InitStruct);

    // 配置输出比较单元
    LL_TIM_OC_InitTypeDef TIM_OC_InitStruct;
    LL_TIM_OC_StructInit(&TIM_OC_InitStruct);
    TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1;
    TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_LOW;
    TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE;
    TIM_OC_InitStruct.CompareValue = 0;   // CCR
    LL_TIM_OC_Init(TIM3, LL_TIM_CHANNEL_CH2, &TIM_OC_InitStruct);

    // 其它寄存器使能
    LL_TIM_EnableCounter(TIM3);

    // 配置 GPIO 为复用推挽输出。使用 TIM3 的 通道 2，对应于 PA4、PA7、PB5、PC7、PE3 的 AF2 复用功能引脚
    LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA);
    LL_GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.Pin = LL_GPIO_PIN_4;
    GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;  // 复用功能
    GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;
    GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
    GPIO_InitStruct.Alternate = LL_GPIO_AF_2;       // 指定复用功能序号
    LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

/**
 * @brief   舵机角度设置
 *
 * @note    舵机角度为 0 ~ 180 度，周期为 20ms，中期高电平为 0.5 ~ 2.5ms，则：
 *          占空比为 2.5 ~ 12.5，则 CCR 为 500 ~ 2500
 *          PWM 频率 = CK_PSC / (PSC + 1) / (ARR + 1)
 *          PWM 占空比 = CCR / (ARR + 1)
 *          PWM 分辨率 = 1 / (ARR + 1)
 * @param   无
 * @retval  无
 */
void HD_Servo_SetAngle(float angle) {
    LL_TIM_OC_SetCompareCH2(TIM3, (uint32_t) (angle / 180 * 2000 + 500));
}

/** region public function region */


/** region private function region */

/** region private function region */
